package utils

import (
	"fmt"
	"go_web/pkg/utils"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
	"time"

	"gorm.io/gorm"
)

// ColumnInfo 存储列信息
type ColumnInfo struct {
	Name            string  `gorm:"column:COLUMN_NAME" json:"name"`
	DataType        string  `gorm:"column:DATA_TYPE" json:"data_type"`
	ColumnType      string  `gorm:"column:COLUMN_TYPE"` // 比较完整的类型 tinyint(1)
	IsNullable      string  `gorm:"column:IS_NULLABLE" json:"is_nullable"`
	ColumnKey       string  `gorm:"column:COLUMN_KEY" json:"column_key"`
	ColumnDefault   *string `gorm:"column:COLUMN_DEFAULT" json:"column_default"`
	Extra           string  `gorm:"column:EXTRA" json:"extra"`
	Comment         string  `gorm:"column:COLUMN_COMMENT" json:"comment"`
	MaxLength       *int    `gorm:"column:CHARACTER_MAXIMUM_LENGTH" json:"max_length"`
	IsExcluded      bool    // 新增字段用于标记是否排除字段
	ForceType       string  // 强制类型：指定某些字段必须为传入类型
	ExcludeFromJSON bool    // 是否排除 JSON 序列化
}

// TableInfo 存储表信息
type TableInfo struct {
	TableName    string `gorm:"column:TABLE_NAME" json:"table_name"`
	TableComment string `gorm:"column:TABLE_COMMENT" json:"table_comment"`
}

// ModelConfig 模型生成配置
type ModelConfig struct {
	DB                *gorm.DB
	DatabaseName      string
	PackageName       string
	OutputPath        string
	Tables            []string      // 指定要生成的表，空表示所有表
	IgnoreFields      []string      // 忽略的字段名
	EmbedStructs      []EmbedStruct // 嵌入的结构体
	ColumnInfos       []ColumnInfo  // 传递字段的配置
	IgnoreStructsTags []string      // 不需要 structs.Map() 转换的字段
}

// EmbedStruct 嵌入的结构体配置
type EmbedStruct struct {
	Name string // 结构体名称
	Type string // 结构体类型
}

// ModelTemplateData 模板数据
type ModelTemplateData struct {
	PackageName       string
	StructName        string
	TableName         string
	TableComment      string
	Columns           []ColumnInfo
	EmbedStructs      []EmbedStruct
	ImportTime        bool
	ImportSQL         bool
	ImportGORM        bool
	GenerateTime      string
	IgnoreStructsTags []string // 不需要 structs.Map() 转换的字段
}

// GenerateModels 生成GORM实体类
func GenerateModels(config ModelConfig) error {
	// 获取表信息
	tableList, err := getTables(config.DB, config.DatabaseName, config.Tables)
	if err != nil {
		return fmt.Errorf("获取表信息失败: %v", err)
	}

	// 确保输出目录存在
	if err := os.MkdirAll(config.OutputPath, 0755); err != nil {
		return fmt.Errorf("创建输出目录失败: %v", err)
	}

	// 遍历表生成模型
	for _, table := range tableList {
		columns, err := getColumns(config.DB, config.DatabaseName, table.TableName)
		if err != nil {
			return fmt.Errorf("获取表 %s 的列信息失败: %v", table.TableName, err)
		}

		// 根据字段配置调整列信息
		columns = adjustColumnsWithConfig(columns, config.ColumnInfos)

		// 生成实体文件
		if err := generateModelFile(config, table, columns); err != nil {
			return fmt.Errorf("生成表 %s 的实体文件失败: %v", table.TableName, err)
		}
	}

	return nil
}

// 根据 ColumnInfos 配置调整列信息（例如，排除 JSON 序列化或强制字段类型）
func adjustColumnsWithConfig(columns []ColumnInfo, columnInfos []ColumnInfo) []ColumnInfo {
	for i, col := range columns {
		for _, config := range columnInfos {
			if col.Name == config.Name {
				if config.ExcludeFromJSON {
					columns[i].ExcludeFromJSON = true // 排除序列化
				}
				if config.ForceType != "" {
					columns[i].ForceType = config.ForceType // 强制类型
				}
			}
		}
	}
	return columns
}

// filterIgnoredFields 过滤忽略的字段
func filterIgnoredFields(columns []ColumnInfo, ignoreFields []string) []ColumnInfo {
	if len(ignoreFields) == 0 {
		return columns
	}

	var filtered []ColumnInfo
	for _, col := range columns {
		ignore := false
		for _, ignoreField := range ignoreFields {
			if strings.EqualFold(col.Name, ignoreField) {
				ignore = true
				break
			}
		}
		if !ignore {
			filtered = append(filtered, col)
		}
	}
	return filtered
}

// generateModelFile 生成单个实体文件
func generateModelFile(config ModelConfig, table TableInfo, columns []ColumnInfo) error {
	// 过滤忽略字段
	columns = filterIgnoredFields(columns, config.IgnoreFields)

	// 准备模板数据
	templateData := prepareTemplateData(config, table, columns)

	// 生成文件名：sys_user -> sys_user.go
	fileName := fmt.Sprintf("%s.go", toSnakeCase(table.TableName))
	filePath := filepath.Join(config.OutputPath, fileName)

	// 检查文件是否已存在
	if _, err := os.Stat(filePath); err == nil {
		fmt.Printf("文件已存在，将覆盖: %s\n", filePath)
	}

	// 创建文件
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("创建文件失败: %v", err)
	}
	defer file.Close()

	// 解析并执行模板
	tmpl := template.Must(template.New("model").Funcs(template.FuncMap{
		"toCamelCase": toCamelCase,
		"goType":      goType,
		"generateTag": generateTag, // 使用统一的标签生成函数
	}).Parse(modelTemplate))

	if err := tmpl.Execute(file, templateData); err != nil {
		return fmt.Errorf("执行模板失败: %v", err)
	}

	fmt.Printf("生成实体文件: %s\n", filePath)
	return nil
}

// prepareTemplateData 准备模板数据
func prepareTemplateData(config ModelConfig, table TableInfo, columns []ColumnInfo) ModelTemplateData {
	data := ModelTemplateData{
		PackageName:  config.PackageName,
		StructName:   toCamelCase(table.TableName),
		TableName:    table.TableName,
		TableComment: table.TableComment,
		Columns:      columns,
		EmbedStructs: config.EmbedStructs,
		GenerateTime: time.Now().Format("2006-01-02 15:04:05"),
		ImportGORM:   true, // 默认需要导入gorm
	}

	// 检查是否需要导入其他包
	for _, col := range columns {
		if col.DataType == "timestamp" || col.DataType == "datetime" || col.DataType == "date" {
			data.ImportTime = true
		}
		if strings.Contains(col.DataType, "text") || strings.Contains(col.DataType, "blob") {
			data.ImportSQL = true
		}
	}

	// 传递 IgnoreStructsTags

	data.IgnoreStructsTags = config.IgnoreStructsTags

	return data
}

// toSnakeCase 将表名转换为蛇形命名（保持不变）
func toSnakeCase(s string) string {
	return s
}

// toCamelCase 将下划线命名转换为大驼峰命名
func toCamelCase(s string) string {
	if strings.ToLower(s) == "id" {
		return "ID"
	}
	parts := strings.Split(s, "_")
	for i := range parts {
		if parts[i] != "" {
			parts[i] = strings.ToUpper(parts[i][:1]) + parts[i][1:]
		}
	}
	return strings.Join(parts, "")
}

func ExtractTypeLengthSafe(columnType string) int {
	if columnType == "" {
		return 0
	}

	// 使用 strings.Cut 来解析
	if _, after, found := strings.Cut(columnType, "("); found {
		if lengthStr, _, found2 := strings.Cut(after, ")"); found2 {
			if length, err := strconv.Atoi(lengthStr); err == nil {
				return length
			}
		}
	}

	return 0
}

// goType 将数据库类型转换为Go类型
func goType(dataType, columnType, isNullable string, maxLength *int, forceType string, columnDefault *string) string {
	if forceType != "" {
		// 如果指定了 ForceType，直接返回强制类型
		return forceType
	}

	var goType string
	switch dataType {
	case "int", "integer", "tinyint", "smallint", "mediumint":
		if ExtractTypeLengthSafe(columnType) == 1 {
			goType = "bool" // 对于长度为1的整型，认为是布尔类型
		} else {
			goType = "int"
		}
	case "bigint":
		goType = "int64"
	case "float", "double", "decimal":
		goType = "float64"
	case "varchar", "char", "text", "longtext", "mediumtext", "tinytext":
		goType = "string"
	case "datetime", "timestamp":
		goType = "time.Time"
	case "date":
		goType = "time.Time"
	case "boolean", "bool":
		goType = "bool"
	case "json":
		goType = "datatypes.JSON" // 这个是 gorm 独有的，很方便就解析 json
	case "blob", "longblob", "mediumblob", "tinyblob":
		goType = "[]byte"
	default:
		goType = "string"
	}

	// 处理可空类型
	if isNullable == "YES" && columnDefault == nil {
		switch goType {
		case "[]byte":
			// 字符串和字节切片本身就可以为null
		case "time.Time":
			goType = "*time.Time"
		case "interface{}":
			// 接口类型本身就可以为null
		default:
			goType = "*" + goType
		}
	}

	return goType
}

// generateTag 生成完整的GORM, JSON, 和 structs 标签
func generateTag(col ColumnInfo, ignoreStructsTags []string) string {
	var gormTags []string

	// 基础列名
	gormTags = append(gormTags, fmt.Sprintf("column:%s", col.Name))

	// 主键
	if col.ColumnKey == "PRI" {
		gormTags = append(gormTags, "primaryKey")
	}

	// 自增
	if col.Extra == "auto_increment" {
		gormTags = append(gormTags, "autoIncrement")
	}

	// 是否为删除标记
	if col.Name == "is_deleted" {
		gormTags = append(gormTags, "softDelete:flag")
	}

	// 是否可为空
	if col.IsNullable == "NO" {
		gormTags = append(gormTags, "not null")
	}

	// 默认值
	if col.ColumnDefault != nil && *col.ColumnDefault != "" {
		defaultValue := *col.ColumnDefault
		if col.DataType == "varchar" || col.DataType == "char" || col.DataType == "text" {
			if defaultValue != "CURRENT_TIMESTAMP" {
				defaultValue = fmt.Sprintf("'%s'", defaultValue)
			}
		}
		gormTags = append(gormTags, fmt.Sprintf("default:%s", defaultValue))
	}

	// 注释
	if col.Comment != "" {
		gormTags = append(gormTags, fmt.Sprintf("comment:%s", col.Comment))
	}

	// 类型约束
	if col.DataType == "datetime" || col.DataType == "timestamp" {
		gormTags = append(gormTags, "type:datetime")
	} else if col.DataType == "date" {
		gormTags = append(gormTags, "type:date")
	}

	// 先获取Go类型，用于判断是否需要添加,string选项
	goType := goType(col.DataType, col.ColumnType, col.IsNullable, col.MaxLength, col.ForceType, col.ColumnDefault)

	// JSON标签处理
	jsonTag := ""
	if col.ExcludeFromJSON {
		jsonTag = `json:"-"` // 排除JSON序列化
	} else {
		jsonFieldName := utils.SnakeToCamel(col.Name)

		// 为int64和*int64类型添加,string选项
		if goType == "int64" || goType == "*int64" {
			jsonTag = fmt.Sprintf(`json:"%s,string"`, jsonFieldName)
		} else {
			jsonTag = fmt.Sprintf(`json:"%s"`, jsonFieldName)
		}
	}

	// structs标签处理
	structsTag := `structs:"` + col.Name + `"` // 默认给字段设置 struct 标签

	// 只有当字段不在 IgnoreStructsTags 中时，才设置 structs 标签
	for _, ignoreField := range ignoreStructsTags {
		if strings.EqualFold(col.Name, ignoreField) {
			structsTag = `structs:"-"` // 排除该字段的 struct 标签
			break
		}
	}

	// 合并标签
	return fmt.Sprintf("`gorm:\"%s\" %s %s`", strings.Join(gormTags, ";"), jsonTag, structsTag)
}

// 模型模板
const modelTemplate = `// Code generated by model-gen. DO NOT EDIT.
// Generated at: {{.GenerateTime}}
package {{.PackageName}}
{{if .ImportTime}}
import "time"{{end}}
{{if .ImportSQL}}
import "database/sql"{{end}}

import "gorm.io/plugin/soft_delete"
import "gorm.io/plugin/optimisticlock"

// {{.StructName}} {{.TableComment}}
type {{.StructName}} struct {
	{{range .EmbedStructs}}
	{{.Name}} {{.Type}}
	{{end}}
	{{range .Columns}}
	// {{.Comment}}
	{{- $goType := goType .DataType .ColumnType .IsNullable .MaxLength .ForceType .ColumnDefault}}
	{{toCamelCase .Name}} {{$goType}} {{generateTag . $.IgnoreStructsTags}}
	{{end}}
}

// TableName 指定表名
func ({{.StructName}}) TableName() string {
	return "{{.TableName}}"
}
`

// getTables 获取表信息
func getTables(db *gorm.DB, dbName string, tableNames []string) ([]TableInfo, error) {
	var tables []TableInfo
	q := db.Table("INFORMATION_SCHEMA.TABLES").
		Select("TABLE_NAME, TABLE_COMMENT").
		Where("TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE'", dbName)

	if len(tableNames) > 0 {
		q = q.Where("TABLE_NAME IN (?)", tableNames)
	}

	if err := q.Find(&tables).Error; err != nil {
		return nil, fmt.Errorf("查询表信息失败: %v", err)
	}

	return tables, nil
}

// getColumns 获取表的列信息（包含完整类型信息）
func getColumns(db *gorm.DB, dbName string, tableName string) ([]ColumnInfo, error) {
	var columns []ColumnInfo
	err := db.Table("INFORMATION_SCHEMA.COLUMNS").
		Select("COLUMN_NAME, DATA_TYPE, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, "+
			"COLUMN_DEFAULT, EXTRA, COLUMN_COMMENT, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE").
		Where("TABLE_SCHEMA = ? AND TABLE_NAME = ?", dbName, tableName).
		Order("ORDINAL_POSITION").
		Find(&columns).Error

	if err != nil {
		return nil, fmt.Errorf("查询列信息失败: %v", err)
	}

	return columns, nil
}

// IsAutoIncrement 检查列是否为自增
func (c *ColumnInfo) IsAutoIncrement() bool {
	return c.Extra == "auto_increment"
}

// HasDefault 检查列是否有默认值
func (c *ColumnInfo) HasDefault() bool {
	return c.ColumnDefault != nil
}

// GetDefaultValue 获取默认值
func (c *ColumnInfo) GetDefaultValue() string {
	if c.ColumnDefault == nil {
		return ""
	}
	return *c.ColumnDefault
}

// GetMaxLength 获取最大长度
func (c *ColumnInfo) GetMaxLength() int {
	if c.MaxLength == nil {
		return 0
	}
	return *c.MaxLength
}
